module BindingsHelper
  # Input: Array of arrays: [[obj1, obj2, obj3], [obj4, obj5]]
  # Output: [[obj1, obj4], [obj2, obj4], [obj3, obj4], [obj1, obj5], [obj2, obj5], [obj3, obj5]]
  # if obj1 == obj4, [obj1, obj4] isn't count as combination
  def combinations_from_array(array_of_arrays)
    combs = []
    if array_of_arrays.size == 2
      array_of_arrays[0].each do |a1|
        array_of_arrays[1].each do |a2|
          combs << [a1, a2].sort{|a,b| a.id <=> b.id}
        end
      end
    elsif array_of_arrays.size == 1
      combs = []
      array_of_arrays[0].each do |a1|
        combs << [a1]
      end
    elsif array_of_arrays.size > 2
      removed_array = array_of_arrays.pop
      combinations_from_array(array_of_arrays).each do |a1|
        removed_array.each do |rm_el|
          combs << (a1 + [rm_el]).sort{|a,b| a.id <=> b.id}
        end
      end
    end
    
    #remove combinations, where two objets are same (if obj1 == obj4, [obj1, obj4] isn't count as combination)
    #TODO more efficient
    combs.select do |comb|
      comb.uniq.size == comb.size
    end
  end
  
  # Reevaluates all token combinations, by setting the only_modified flag the reevaluation can be limited
  # to modified tokens.
  def reevaluate!(only_modified = false)
    if !only_modified
      #deletes all combinations without callbacks (if callback needed, use destroy_all)
      combinations.delete_all
      #FIXME needed?
      reload
    end
    
    combs = []
    incoming_places(:for_combinations_only => true).each_with_index do |place, i|
      tokens = if only_modified
        modtokens.collect{|mod_token| mod_token.token}.select{|token| token.place == place}
      else
        place.tokens
      end
      #incoming_places-[place] can't be used, because in case of multiples arcs (all occurences of place would be removed)
      places = incoming_places(:for_combinations_only => true).clone
      places.delete_at(i)
      combs += combinations_from_array([tokens] + places.collect{|p|p.tokens})
    end
    
    #TODO remove duplicates more efficiently/ much earlier!! Therefore, reevaluate must work together with 
    #     combinations_from_array
    combs.uniq!
    
    combs.each do |comb|
      if eval_guard(comb)
        #store combinations for caching purposes
        self.combinations.create!(:tokens => comb)
        PNEngine::CsvLogger.log_transition("activate", Time.new.to_s, $user && $user.name, self.id, self.name, self.action, comb.first.nil? ? 0 : comb.first.case.id, self.petrinet.id)
      end
    end
    
    # Because of recalculation no mod_tokens exist anymore
    # TODO if mod_tokens are deleted while refactoring to a many-to-many table, delete_all can't be used
    # anymore, because tokens would be deleted instead of mod_tokens tuple
    modtokens.delete_all
  end
  
  #gets the token combinations suitable for firing the transition
  def token_combinations(force_update = false)
    reevaluate!(!force_update)
    combinations
  end
end